#include <windows.h>
#include "Helix.h"
#include "HObject.h"
#include "Application.h"
#include "Window.h"
#include "GLWindow.h"
#include "Util/PointerCheck.h"
#include "Input.h"
#include "Timer.h"
#include "Renderer.h"
#include "Updater.h"

#include <gl/gl.h>

namespace helix
{

	Application* Application::thisApp=new Application();

	Application::Application()			//make the constructor private so that the user can't create more than one
	{
		appWindow=NULL;
		appInput=NULL;
		appTimer=NULL;
		appUpdater=NULL;
		appRenderer=NULL;
	}

	Application::~Application()
	{
		if(appWindow)
			delete appWindow;

		if(appInput)
			delete appInput;

		if(appTimer)
			delete appTimer;

		if(appRenderer)
			delete appRenderer;

		if(appUpdater)
			delete appUpdater;

		if (!UnregisterClass("Helix",hInstance))			// Are We Able To Unregister Class
		{
			MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
			Log().Error("Could not unregister the class");
			hInstance=NULL;									// Set hInstance To NULL
		}
	}

	Application* Application::getInstance()
	{
		return thisApp;
	}
		
	bool Application::initApp(const char* fileName)
	{
		//does nothing for now,  will eventually read an xml file for initialization
		Log().Error("Cannot initialize application from xml file.  Function is not implemented yet.");
		return false;
	}

	bool Application::initApp(int windowType,const char* title,int width,int height,int bpp,float fov,bool fullScreen)
	{
		this->appWindow=NULL;
		running=true;
		active=true;
		
		hInstance			= GetModuleHandle(NULL);				// Grab An Instance For Our Window
		wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
		wc.lpfnWndProc		= (WNDPROC) handleMessages;					// WndProc Handles Messages
		wc.cbClsExtra		= 0;									// No Extra Window Data
		wc.cbWndExtra		= 0;									// No Extra Window Data
		wc.hInstance		= hInstance;							// Set The Instance
		wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);			// Load The Default Icon
		wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			// Load The Arrow Pointer
		wc.hbrBackground	= NULL;									// No Background Required For GL
		wc.lpszMenuName		= NULL;									// We Don't Want A Menu
		wc.lpszClassName	= "Helix";								// Set The Class Name

		if (!RegisterClass(&wc))									// Attempt To Register The Window Class
		{
			Log().Error("Failed To Register The Window Class.");
			return false;											
		}

		Log().Println("Registered The Window Class.");

		switch(windowType)
		{
			case OPEN_GL_WINDOW:
				appWindow=new GLWindow(hInstance,title,width,height,bpp,fov,fullScreen);
				break;
			default:
				Log().Error("Could not create window, specified type does not exist.");
				return false;
		}

		appWindow->initWindow();

		Log().Println("Created the window.");

		if(appWindow->isCreated())
		{
			appWindow->setWindowParams(width,height);
			appWindow->resizeScene();									// Set Up Our Perspective GL Screen
			HWND hWnd=appWindow->HWND();
			ShowWindow(hWnd,SW_SHOW);						// Show The Window
			SetForegroundWindow(hWnd);						// Slightly Higher Priority
			SetFocus(hWnd);									// Sets Keyboard Focus To The Window
		}

		return appWindow->isCreated();
	}

	bool Application::runMainLoop(MSG *msg)
	{
		if (PeekMessage(msg,NULL,0,0,PM_REMOVE))	// Is There A Message Waiting?
		{
			if (msg->message==WM_QUIT)				// Have We Received A Quit Message?
			{
				running=false;						// If So then we are done
			}
			else									// If Not, Deal With Window Messages
			{
				TranslateMessage(msg);				// Translate The Message
				DispatchMessage(msg);				// Dispatch The Message
			}
		}
		else if(active)								// If There Are No Messages
		{
			//The main game loop this code gets called every frame
			double seconds=H_DEFAULT_DSECONDS;
			if(appTimer)
			{
				appTimer->updateTime();
				seconds=appTimer->dSeconds();
			}
			if(appInput)
			{
				bool exit=false;
				appInput->update();
				exit=appInput->keyDown(H_ESCAPE);
				if(exit)
					exitApp(0);
			}
			// Draw The Scene.  
			if (running)								// Program Active?
			{
				if(appUpdater)
					appUpdater->update(seconds);
				if(appRenderer)
					appRenderer->renderScene();

				//DrawGLScene();					// Draw The Scene
				SwapBuffers(appWindow->HDC());				// Swap Buffers (Double Buffering)
			}
		}

		return running;
	}

	void Application::setInput(Input *input)
	{
		if(appInput)
			delete appInput;
		appInput=input;
	}

	Input *Application::input()
	{
		return appInput;
	}

	void Application::deleteInput()
	{
		if(appInput)
			delete appInput;
		appInput=NULL;
	}

	//set the timer object that gets updated every frame
	void Application::setTimer(Timer *timer)				
	{
		appTimer=timer;
	}

	//get the timer object
	Timer *Application::timer()							
	{
		return appTimer;
	}

	//delete the current timer object
	void Application::deleteTimer()							
	{
		if(appTimer)
			delete appTimer;
		appTimer=NULL;
	}

	//set the updater object that gets called every frame
	void Application::setUpdater(Updater *updater)
	{
		appUpdater=updater;
	}

	//get the updater object
	Updater *Application::updater()						
	{
		return appUpdater;
	}

	//delete the current updater object
	void Application::deleteUpdater()		
	{
		if(appUpdater)
			delete appUpdater;
		appUpdater=NULL;
	}

	//replace the current updater object (old object is deleted)
	void Application::replaceUpdater(Updater *updater)		
	{
		deleteUpdater();
		appUpdater=updater;
	}

	//set the renderer object that gets called every frame
	void Application::setRenderer(Renderer *renderer)			
	{
		appRenderer=renderer;
	}

	//get the renderer object
	Renderer *Application::renderer()
	{
		return appRenderer;
	}

	//delete the current renderer object
	void Application::deleteRenderer()		
	{
		if(appRenderer)
			delete appRenderer;
		appRenderer=NULL;
	}

	//replace the current renderer object (old object is deleted)
	void Application::replaceRenderer(Renderer *renderer)
	{
		deleteRenderer();
		appRenderer=renderer;
	}

	void Application::exitApp(int exitCode)
	{
		Log().Println("Application is exiting with exit code: %d",exitCode);
		running=false;	//flag the application to stop running
	}

	void Application::destroyApp()
	{
		appWindow->killWindow();
		Log().releaseLog();
	}

	LRESULT Application::handleMessages(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
	{
		switch (uMsg)									// Check For Windows Messages
		{
			case WM_ACTIVATE:							// Watch For Window Activate Message
			{
				if (!HIWORD(wParam))					// Check Minimization State
				{
					thisApp->active=true;						// Program Is Active
				}
				else
				{
					thisApp->active=false;						// Program Is No Longer Active
				}

				return 0;								// Return To The Message Loop
			}

			case WM_SYSCOMMAND:							// Intercept System Commands
			{
				switch (wParam)							// Check System Calls
				{
					case SC_SCREENSAVE:					// Screensaver Trying To Start?
					case SC_MONITORPOWER:				// Monitor Trying To Enter Powersave?
					return 0;							// Prevent From Happening
				}
				break;									// Exit
			}

			case WM_CLOSE:								// Did We Receive A Close Message?
			{
				PostQuitMessage(0);						// Send A Quit Message
				return 0;								// Jump Back
			}

			case WM_SIZE:								// Resize The OpenGL Window
			{
				thisApp->appWindow->setWindowParams(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height
				thisApp->appWindow->resizeScene();							
				return 0;								// Jump Back
			}
		}

		// Pass All Unhandled Messages To DefWindowProc
		return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}

}